<template>
  <div ref="wrapper" class="wrapper" @mousewheel.prevent="handleMouseWheel">
    <template v-if="props.showArrow && isOverflow">
      <div class="left dark:bg-dark!" @click="handleMouseWheel(120)">
        <icon-ic:baseline-keyboard-arrow-left />
      </div>
      <div class="right dark:bg-dark!" @click="handleMouseWheel(-120)">
        <icon-ic:baseline-keyboard-arrow-right />
      </div>
    </template>

    <div
      ref="content"
      class="content"
      :class="{ overflow: isOverflow && props.showArrow }"
      :style="{
        transform: `translateX(${translateX}px)`
      }"
    >
      <slot />
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onBeforeUnmount, onMounted } from 'vue'
import { useDebounceFn, useResizeObserver, type UseResizeObserverReturn } from '@vueuse/core'

const props = defineProps({
  showArrow: {
    type: Boolean,
    default: true
  }
})

const translateX = ref(0)
const content = ref<HTMLElement | null>(null)
const wrapper = ref<HTMLElement | null>(null)
const isOverflow = ref(false)

const resetTranslateX = useDebounceFn(function (wrapperWidth: number, contentWidth: number) {
  if (!isOverflow.value) {
    translateX.value = 0
  } else if (-translateX.value > contentWidth - wrapperWidth) {
    translateX.value = wrapperWidth - contentWidth
  } else if (translateX.value > 0) {
    translateX.value = 0
  }
}, 200)

const refreshIsOverflow = useDebounceFn(() => {
  const wrapperWidth = wrapper.value?.offsetWidth
  const contentWidth = content.value?.offsetWidth
  isOverflow.value = contentWidth! > wrapperWidth!
  resetTranslateX(wrapperWidth!, contentWidth!)
}, 200)

function handleMouseWheel(wheelDelta: number) {
  const wrapperWidth = wrapper.value?.offsetWidth
  const contentWidth = content.value?.offsetWidth
  if (wheelDelta! < 0) {
    if (wrapperWidth! > contentWidth! && translateX.value < -10) return
    if (wrapperWidth! <= contentWidth! && contentWidth! + translateX.value - wrapperWidth! < -10) return
  }
  if (wheelDelta! > 0 && translateX.value > 10) {
    return
  }

  translateX.value += wheelDelta!
  resetTranslateX(wrapperWidth!, contentWidth!)
}

const observer = ref<UseResizeObserverReturn | null>(null)

onMounted(() => {
  refreshIsOverflow()
  // @ts-ignore
  observer.value = useResizeObserver(document.body, refreshIsOverflow)
})
onBeforeUnmount(() => {
  observer.value?.stop()
})

function handleScroll(x: number, width: number) {
  const wrapperWidth = wrapper.value?.offsetWidth
  const contentWidth = content.value?.offsetWidth
  if (contentWidth! <= wrapperWidth!) return

  if (x < -translateX.value + 150) {
    translateX.value = -(x - 150)
    resetTranslateX(wrapperWidth!, contentWidth!)
  }

  if (x + width > -translateX.value + wrapperWidth!) {
    translateX.value = wrapperWidth! - (x + width)
    resetTranslateX(wrapperWidth!, contentWidth!)
  }
}

export type ScrollX = {
  handleScroll: (x: number, width: number) => void
  refreshIsOverflow: () => void
}

defineExpose<ScrollX>({
  handleScroll,
  refreshIsOverflow
})
</script>

<style lang="scss" scoped>
.wrapper {
  position: relative;
  z-index: 9;
  display: flex;
  overflow: hidden;
  background-color: #fff;

  .content {
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    padding: 0 10px;
    transition: transform 0.5s;

    &.overflow {
      padding-right: 30px;
      padding-left: 30px;
    }
  }

  .left,
  .right {
    position: absolute;
    top: 0;
    bottom: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 35px;
    margin: auto;
    font-size: 18px;
    cursor: pointer;
    background-color: #fff;
    border: 1px solid #e0e0e6;
    border-radius: 2px;
  }

  .left {
    left: 0;
  }

  .right {
    right: 0;
  }
}
</style>
